/******************************************************************************
 * 
 * Announce: CSharpKit, Basic algorithms, components and definitions.
 *           Copyright (C) ShenYongchen.
 *           All rights reserved.
 *   Author: 申永辰.郑州 (shenyczz@163.com)
 *  WebSite: http://github.com/shenyczz/CSharpKit
 *
 * THIS CODE IS LICENSED UNDER THE MIT LICENSE (MIT).
 * THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF 
 * ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
 * IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
 * PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
 * 
******************************************************************************/
using System;
using System.Globalization;
using System.IO;

namespace CSharpKit.SVM
{
    /// <summary>
    /// 名称: 高斯变换<br/>
    /// 功能: A transform which learns the mean and variance of a sample set and uses these to transform new data
    /// so that it has zero mean（零均值） and unit variance（单位方差）.
    /// </summary>
    public class GaussianTransform : IRangeTransform
    {
        #region Constructors
            
        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="means">Means in each dimension</param>
        /// <param name="stddevs">Standard deviation in each dimension</param>
        public GaussianTransform(double[] means, double[] stddevs)
        {
            _means = means;
            _stddevs = stddevs;
        }

        #endregion

        #region Fields

        private double[] _means;    // 每个维度的均值
        private double[] _stddevs;  // 每个维度的标准偏差

        #endregion

        #region IRangeTransform Members

        public double Transform(double input, int index)
        {
            index--;

            if (_stddevs == null || _stddevs[index] == 0)
                return 0;

            double diff = input - _means[index];
            diff /= _stddevs[index];
            return diff;
        }

        public SvmNode[] Transform(SvmNode[] input)
        {
            SvmNode[] output = new SvmNode[input.Length];
            for (int i = 0; i < output.Length; i++)
            {
                int index = input[i].Index;
                double value = input[i].Value;
                output[i] = new SvmNode(index, Transform(value, index));
            }
            return output;
        }

        #endregion


        /// <summary>
        /// 对样本数据集进行高斯变换
        /// </summary>
        /// <param name="prob"></param>
        /// <returns></returns>
        public static GaussianTransform Compute(SvmProblem prob)
        {
            int[] counts = new int[prob.MaxIndex];      // 每个维度数量

            // 均值
            double[] means = new double[prob.MaxIndex];
            foreach (SvmNode[] sample in prob.X)
            {
                for (int i = 0; i < sample.Length; i++)
                {
                    means[sample[i].Index - 1] += sample[i].Value;
                    counts[sample[i].Index - 1]++;
                }
            }

            for (int i = 0; i < prob.MaxIndex; i++)
            {
                if (counts[i] == 0)
                    counts[i] = 2;
                means[i] /= counts[i];
            }

            // 标准偏差
            double[] stddevs = new double[prob.MaxIndex];
            foreach (SvmNode[] sample in prob.X)
            {
                for (int i = 0; i < sample.Length; i++)
                {
                    double diff = sample[i].Value - means[sample[i].Index - 1];
                    stddevs[sample[i].Index - 1] += diff * diff;
                }
            }
            for (int i = 0; i < prob.MaxIndex; i++)
            {
                if (stddevs[i] == 0)
                    continue;
                stddevs[i] /= (counts[i] - 1);
                stddevs[i] = System.Math.Sqrt(stddevs[i]);
            }

            return new GaussianTransform(means, stddevs);
        }



        // I/O

        public static GaussianTransform Read(string filename)
        {
            FileStream input = File.Open(filename, FileMode.Open);
            try
            {
                return Read(input);
            }
            finally
            {
                input.Close();
            }
        }
        public static GaussianTransform Read(Stream stream)
        {
            TemporaryCulture.Start();

            StreamReader input = new StreamReader(stream);
            int length = int.Parse(input.ReadLine(), CultureInfo.InvariantCulture);
            double[] means = new double[length];
            double[] stddevs = new double[length];
            for (int i = 0; i < length; i++)
            {
                string[] parts = input.ReadLine().Split();
                means[i] = double.Parse(parts[0], CultureInfo.InvariantCulture);
                stddevs[i] = double.Parse(parts[1], CultureInfo.InvariantCulture);
            }

            TemporaryCulture.Stop();

            return new GaussianTransform(means, stddevs);
        }

        public static void Write(string filename, GaussianTransform transform)
        {
            FileStream output = File.Open(filename, FileMode.Create);
            try
            {
                Write(output, transform);
            }
            finally
            {
                output.Close();
            }
        }
        public static void Write(Stream stream, GaussianTransform transform)
        {
            TemporaryCulture.Start();

            StreamWriter output = new StreamWriter(stream);
            output.WriteLine(transform._means.Length);
            for (int i = 0; i < transform._means.Length; i++)
                output.WriteLine("{0} {1}", transform._means[i], transform._stddevs[i]);
            output.Flush();

            TemporaryCulture.Stop();
        }


        //}}@@@
    }





}

